﻿using Microsoft.EntityFrameworkCore;
using StarsAdmin.Core.DB;
using StarsAdmin.Core.DB.Entities;
using StarsAdmin.Core.DB.Extension;
using System.Linq.Expressions;

namespace StarsAdmin.Core.BaseRepository
{
    public class BaseRepository<TEntity> : IBaseRepository<TEntity> where TEntity : BaseEntity
    {
        private readonly DBContext _dbContext;

        public BaseRepository(DBContext dBContext)
        {
            _dbContext = dBContext;
        }

        public bool UseTransaction(Action action)
        {
            return _dbContext.NewTransaction(action);
        }

        public async Task<bool> UseTransactionAsync(Func<Task> func)
        {
            return await _dbContext.NewTransactionAsync(func);
        }

        public IQueryable<TEntity> Get()
        {
            return _dbContext.Set<TEntity>();
        }

        public IQueryable<TEntity> GetAll()
        {
            return _dbContext.Set<TEntity>().Where(e => e.IsDeleted == false);
        }

        public IQueryable<TEntity> GetBy(Expression<Func<TEntity, bool>> predicate, bool isTracking = true)
        {
            if (isTracking)
            {
                return GetAll().AsNoTracking().Where(predicate);
            } else
            {
                return GetAll().Where(predicate);
            }
        }

        public IQueryable<TEntity> GetById(long id)
        {
            return GetAll().Where(m => m.Id == id);
        }

        public async Task<TEntity?> GetByIdAsync(long id)
        {
            return await GetAll().Where(m => m.Id == id).FirstOrDefaultAsync();
        }

        public IQueryable<TEntity> GetByIds(IEnumerable<long> ids)
        {
            return GetAll().Where(m => ids.Contains(m.Id));
        }

        public async Task<IQueryable<TEntity>> GetByIdsAsync(IEnumerable<long> ids)
        {
            return await Task.FromResult(GetByIds(ids));
        }

        public async Task<bool> AddAsync(TEntity entity)
        {
            try
            {
                await _dbContext.AddAsync(entity);
                return true;
            } catch
            {
                return false;
            }
        }

        public async Task<bool> AddAndSaveAsync(TEntity entity)
        {
            var result = await AddAsync(entity);
            return result ? await SaveChangesAsync() : false;
        }

        public async Task<bool> AddRangeAsync(IEnumerable<TEntity> entities)
        {
            try
            {
                await _dbContext.AddRangeAsync(entities);
                return true;
            } catch
            {
                return false;
            }
        }

        public async Task<bool> AddRangeAndSaveAsync(IEnumerable<TEntity> entities)
        {
            var result = await AddRangeAsync(entities);
            return result ? await SaveChangesAsync() : result;
        }

        public async Task<bool> UpdateAsync(TEntity entity)
        {
            try
            {
                entity.LastUpdateDateTime = DateTime.Now;
                _dbContext.Update(entity);
                return await Task.FromResult(true);
            } catch
            {
                return await Task.FromResult(false);
            }
        }

        public async Task<bool> UpdateAndSaveAsync(TEntity entity)
        {
            var result = await UpdateAsync(entity);
            return result ? await SaveChangesAsync() : result;
        }

        public async Task<bool> AttachAsync(TEntity entity, Expression<Func<TEntity, object>> predicate)
        {
            try
            {
                _dbContext.Attach(entity);
                _dbContext.Entry(entity).Property(predicate).IsModified = true;
                _dbContext.Entry(entity).Property(e => e.LastUpdateDateTime).IsModified = true;
                return await Task.FromResult(true);
            } catch
            {
                return await Task.FromResult(false);
            }
        }

        public async Task<bool> AttachAndSaveAsync(TEntity entity, Expression<Func<TEntity, object>> predicate)
        {
            var result = await AttachAsync(entity, predicate);
            return result ? await SaveChangesAsync() : result;
        }

        public async Task<bool> AttachRangeAsync(IEnumerable<TEntity> entities, Expression<Func<TEntity, object>> predicate)
        {
            try
            {
                _dbContext.AttachRange(entities);
                foreach (var entity in entities)
                {
                    _dbContext.Entry(entity).Property(predicate).IsModified = true;
                    _dbContext.Entry(entity).Property(e => e.LastUpdateDateTime).IsModified = true;
                }
                return await Task.FromResult(true);
            } catch
            {
                return await Task.FromResult(false);
            }
        }

        public async Task<bool> AttachRangeAndSaveAsync(IEnumerable<TEntity> entities, Expression<Func<TEntity, object>> predicate)
        {
            var result = await AttachRangeAsync(entities, predicate);
            return result ? await SaveChangesAsync() : result;
        }

        public async Task<bool> UpdateRangeAsync(IEnumerable<TEntity> entities)
        {
            try
            {
                foreach (var item in entities)
                {
                    item.LastUpdateDateTime = DateTime.Now;
                }
                _dbContext.UpdateRange(entities);
                return await Task.FromResult(true);
            } catch
            {
                return await Task.FromResult(false);
            }
        }

        public async Task<bool> UpdateRangeAndSaveAsync(IEnumerable<TEntity> entities)
        {
            var result = await UpdateRangeAsync(entities);
            return result ? await SaveChangesAsync() : result;
        }

        public async Task<bool> DeleteAsync(long id)
        {
            var entity = await GetByIdAsync(id);
            if (entity is null)
            {
                return false;
            }
            entity.IsDeleted = true;
            return await UpdateAsync(entity);
        }

        public async Task<bool> DeleteAndSaveAsync(long id)
        {
            var result = await DeleteAsync(id);
            return result ? await SaveChangesAsync() : result;
        }

        public async Task<bool> DeleteRangeAsync(IEnumerable<long> ids)
        {
            var dels = await GetByIdsAsync(ids);
            foreach (var item in dels)
            {
                item.IsDeleted = true;
            }
            return await UpdateRangeAsync(dels);
        }

        public async Task<bool> DeleteRangeAndSaveAsync(IEnumerable<long> ids)
        {
            var result = await DeleteRangeAsync(ids);
            return result ? await SaveChangesAsync() : result;
        }

        public async Task<bool> SaveChangesAsync()
        {
            return await _dbContext.SaveChangesAsync() > 0;
        }
    }
}